use syn::{parse_file, Pat, Expr};
use syn::visit::Visit;
use std::fs;
use crate::mir::instr::Operand;
use crate::mir::{self, function, MirBuilder::Context, function::func, basicblock::bb, ScopeStack::ScopeStack, MirBuilder::MirBuilder};
use crate::mir::mir_ref::{InstrRef, MIRSlotMap, FunctionRef};

pub struct LoweToMir<'a> {
    pub funcs: Vec<FunctionRef>,
    // pub mir_ctx : &'a mut function::Context,
    pub scope: ScopeStack,
    pub builder: MirBuilder<'a>,
}

impl<'a> LoweToMir<'a> {
    pub fn new(ctx: &'a mut Context) -> Self {
        Self { 
            funcs: Vec::new(), 
            scope: ScopeStack::new(),
            builder: MirBuilder::new(ctx),
        }
    }

    pub fn lower_expr(&mut self, e: &Expr) -> Option<Operand> {
        match e {
            // the int literal
            syn::Expr::Lit(literal) => {
                if let syn::Lit::Int(int_val) = &literal.lit {
                    let res = Operand::ConstValue(int_val.base10_parse().unwrap());
                    Some(res)
                } else {
                    None
                }
            }
            syn::Expr::Binary(b) => {
                // eprintln!("{:?}", b);
                let bop = b.op;
                
                let bop = match bop {
                    syn::BinOp::Add(_) => { Some(mir::instr::BinOp::Arith(mir::instr::ArithmeticOp::ADD)) }
                    syn::BinOp::Sub(_) => { Some(mir::instr::BinOp::Arith(mir::instr::ArithmeticOp::MINUS)) }
                    syn::BinOp::Mul(_) => { Some(mir::instr::BinOp::Arith(mir::instr::ArithmeticOp::MUL)) }

                    syn::BinOp::Ge(_) => { Some(mir::instr::BinOp::Boolean(mir::instr::BooleanOp::GE)) }
                    syn::BinOp::Gt(_) => { Some(mir::instr::BinOp::Boolean(mir::instr::BooleanOp::GT)) }
                    syn::BinOp::Eq(_) => { Some(mir::instr::BinOp::Boolean(mir::instr::BooleanOp::EQ)) }
                    syn::BinOp::Lt(_) => { Some(mir::instr::BinOp::Boolean(mir::instr::BooleanOp::LT)) }
                    syn::BinOp::Le(_) => { Some(mir::instr::BinOp::Boolean(mir::instr::BooleanOp::LE)) }

                    _ => None
                };

                let instr = match bop {
                    Some(o) => {
                        let lhs = self.lower_expr(&b.left)?;
                        let rhs = self.lower_expr(&b.right)?;

                        let instr_ref = self.builder.create_bin(o, lhs, rhs);
                        Some(Operand::Vreg(instr_ref))
                    }
                    None => None
                };
                
                instr
            }

            syn::Expr::Path(p) => {
                let name = p.path.segments[0].ident.to_string();
                match self.scope.lookup(name.as_str()) {
                    Some(i) => {
                        Some(*i)
                    }
                    None => None
                }
            }

            _ => {None}
        }
    }

    fn parse_fn_args(&mut self, sig: &syn::Signature) -> Vec<(String, mir::mtype::MIRType)> {
        let mut res = Vec::new();
        for arg in &sig.inputs {
            match arg {
                syn::FnArg::Typed(pat_ty) => {
                    // 参数名字
                    if let syn::Pat::Ident(pat_ident) = pat_ty.pat.as_ref() {
                        let name = pat_ident.ident.to_string();

                        // 参数类型
                        let ty = match pat_ty.ty.as_ref() {
                            syn::Type::Path(tp) => {
                                let seg = &tp.path.segments.last().unwrap().ident;
                                match seg.to_string().as_str() {
                                    "i32" => Some(mir::mtype::MIRType::I32),
                                    // 跳过别的，因为只有i32
                                    _ => None,
                                }
                            }
                            _ => {None}
                        };

                        res.push((name, ty.unwrap()));
                    }
                }
                syn::FnArg::Receiver(_) => {
                    // 跳过 self
                }
            }
        }
        res
    }
}

impl<'ast, 'a> Visit<'ast> for LoweToMir<'a> {
    fn visit_item_fn(&mut self, node: &'ast syn::ItemFn) {
        // println!("Found Function: {}", node.sig.ident);
        let func_name = node.sig.ident.to_string();
        let mut ret_ty = mir::mtype::MIRType::VOID;

        match &node.sig.output {
            syn::ReturnType::Default => {}
            syn::ReturnType::Type(_, ty) => {
                match ty.as_ref() {
                    syn::Type::Path(typepath) => {
                        let seg = &typepath.path.segments.last().unwrap().ident;
                        match seg.to_string().as_str() {
                            "i32" => { ret_ty = mir::mtype::MIRType::I32; }
                            other => {}
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }

        let f_f_p = self.parse_fn_args(&node.sig);

        let f = self.builder.create_func(
            func_name, 
            ret_ty, 
            f_f_p.clone());

        self.builder.set_cur_func(f);

        for (p, _) in f_f_p {
            let i = self.builder.create_load(p.clone());
            self.scope.insert(p, mir::instr::Operand::Vreg(i));
        } 

        self.funcs.push(f);

        self.scope.enter_scope(); 
        syn::visit::visit_item_fn(self, node);
        self.scope.exit_scope();
    }


    fn visit_stmt(&mut self, i: &'ast syn::Stmt) {
        // println!("Found a stmt : {:?}", i);

        syn::visit::visit_stmt(self, i);
    }

    fn visit_expr_return(&mut self, i: &'ast syn::ExprReturn) {
         let ret_val = i.expr
            .as_ref()
            .and_then(|e| self.lower_expr(e)); // Option<Operand>

        let i = self.builder.create_return(ret_val);
    }


    fn visit_block(&mut self, i: &'ast syn::Block) {
        self.scope.enter_scope(); 
        syn::visit::visit_block(self, i);
        self.scope.exit_scope();
    }

    fn visit_local(&mut self, i: &'ast syn::Local) {
        if let Pat::Ident(pat_ident) = &i.pat{
            let name = pat_ident.ident.to_string();
            // println!("find var decl {}", name);

            let mut init_val = None;
            if let Some(init) = &i.init {
                init_val = self.lower_expr(&init.expr);
            }

            if let Some(iv) = init_val {
                 self.scope.insert(name, iv);
            }
        }

        syn::visit::visit_local(self, i);
    }
    
    fn visit_expr_if(&mut self, i: &'ast syn::ExprIf) {
        let cond_op = self.lower_expr(&i.cond).unwrap();

        if let Some((_, e_bb)) = &i.else_branch {
            let true_bb = self.builder.create_new_bb();
            let else_bb = self.builder.create_new_bb();
            let end_bb = self.builder.create_new_bb();

            // create the switch instr
            self.builder.create_switch(cond_op, true_bb, Some(else_bb));

            // goto ture branch
            self.builder.set_cur_bb(true_bb);
            let t_bb = &i.then_branch;
            self.visit_block(t_bb);
            self.builder.create_goto(end_bb);

            // goto else branch
            self.builder.set_cur_bb(else_bb);
            match e_bb.as_ref() {
                syn::Expr::Block(b) => {
                    self.visit_block(&b.block);
                    self.builder.create_goto(end_bb);
                }
                _ => {}
            }
            self.builder.set_cur_bb(end_bb);
        } else {
            let true_bb = self.builder.create_new_bb();
            let end_bb = self.builder.create_new_bb();

            self.builder.create_switch(cond_op, true_bb, Some(end_bb));

            // create the switch instr
            // self.builder.create_switch(cond_op, true_bb, None);

            // goto ture branch
            self.builder.set_cur_bb(true_bb);
            let t_bb = &i.then_branch;
            self.visit_block(t_bb);
            self.builder.create_goto(end_bb);
        }
        // syn::visit::visit_expr_if(self, i);
    }
}


pub fn parse_src(file: &str) {
    let code  = fs::read_to_string(file).unwrap();
    let syntax = syn::parse_file(&code).unwrap();

    let mut ctx = Context::new();
    let mut visitor = LoweToMir::new(&mut ctx);
    visitor.visit_file(&syntax);

    // for f in &visitor.funcs {
    //     eprintln!("{:?}", visitor.builder.mir_arena.get_func(*f));
    // }
    let dumper = mir::MirDump::MirDumper::new(&visitor.builder.mir_arena, &visitor.funcs);
    print!("{}", dumper.dump_mir());
}


